/*
 * JHeuristics - The Java Meta-Heuritics Library
 * Copyright (C) 2004-2005, Universidade Federal de Campina Grande. All rights reserved.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
package utils.io;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.StringTokenizer;
import java.util.Vector;

import utils.Graph;
import utils.SimpleGraph;

public class TSPLibReader implements FileReader {
	private static final int TSP = 0;

	private static final int EXPLICIT_TYPE = 0;
	private static final int EUC_2D_TYPE = 1;
	private static final int CEIL_2D_TYPE = 2;

	private static final int UPPER_ROW_FORMAT = 0;

	private static final int NODE_COORD_SECTION = 0;
	private static final int EDGE_WEIGHT_SECTION = 1;

	public Object read(String filepath) throws FileNotFoundException, InvalidFileException {
		URL url;
		try {
			url = FileUtils.locate(filepath, this.getClass());
		} catch (MalformedURLException e) {
			// TODO: ERROR MSG
			throw new FileNotFoundException();
		} catch (IllegalArgumentException e) {
			throw new FileNotFoundException(e.getMessage());
		}
		if (url == null) {
			// TODO: ERROR MSG
			throw new FileNotFoundException();
		}
		File file = new File(url.getFile());;
		return processFile(new BufferedReader(new java.io.FileReader(file)));
	}

	private Object processFile(BufferedReader in) throws InvalidFileException {
		int problemType = -1, dimension = -1, edgeType = -1, edgeFormat = -1;
		String line;
		try {
			while ((line = in.readLine()) != null) {
				StringTokenizer tokenizer = new StringTokenizer(line, ":");
				String keyword = null, value = null;
				if (tokenizer.hasMoreTokens()) {
					keyword = tokenizer.nextToken().trim().toUpperCase();
				}
				if (tokenizer.hasMoreTokens()) {
					value = tokenizer.nextToken().trim().toUpperCase();
				} else {
					value = null;
				}
				if (keyword.startsWith("TYPE")) {
					if (value.startsWith("TSP")) {
						problemType = TSP; 
					}
				} else if (keyword.startsWith("DIMENSION")) {
					dimension = Integer.parseInt(value);
				} else if (keyword.startsWith("EDGE_WEIGHT_TYPE")) {
					if (value.startsWith("EXPLICIT")) {
						edgeType = EXPLICIT_TYPE;
					} else if (value.startsWith("EUC_2D")) {
						edgeType = EUC_2D_TYPE;
					} else if (value.startsWith("CEIL_2D")) {
						edgeType = CEIL_2D_TYPE;
					}
				} else if (keyword.startsWith("EDGE_WEIGHT_FORMAT")) {
					if (value.startsWith("UPPER_ROW")) {
						edgeFormat = UPPER_ROW_FORMAT;
					}
				} else if (keyword.startsWith("NODE_COORD_SECTION")) {
					return processData(in, problemType, dimension, edgeType, edgeFormat, NODE_COORD_SECTION);
				} else if (keyword.startsWith("EDGE_WEIGHT_SECTION")) {
					return processData(in, problemType, dimension, edgeType, edgeFormat, EDGE_WEIGHT_SECTION);
				}
			}
		} catch (Exception e) {
			if (e instanceof InvalidFileException) {
				throw (InvalidFileException) e;
			} else {
				throw new InvalidFileException(e);
			}
		} finally {
			try {
				in.close();
			} catch (IOException e) {
				throw new InvalidFileException(e);
			}
		}
		// TODO: ERROR MSG
		throw new InvalidFileException();
	}

	private Object processData(BufferedReader in, int problemType, int dimension, int edgeType, int edgeFormat, int section) throws InvalidFileException, IOException {
		switch (problemType) {
			case TSP:
				switch (edgeType) {
					case EXPLICIT_TYPE:
						switch (edgeFormat) {
							case UPPER_ROW_FORMAT:
								return read_TSP_EXPLICIT_UPPER_ROW(in, dimension);

							default:
								// TODO: ERROR MSG
								throw new InvalidFileException();
						}

					case EUC_2D_TYPE:
						return read_TSP_EUC_2D(in, dimension);

					case CEIL_2D_TYPE:
						return read_TSP_CEIL_2D(in, dimension);

					default:
						// TODO: ERROR MSG
						throw new InvalidFileException();
				}

			default:
				// TODO: ERROR MSG
				throw new InvalidFileException();
		}
	}

	private Object read_TSP_EXPLICIT_UPPER_ROW(BufferedReader in, int dimension) throws IOException {
		Graph graph = new SimpleGraph();
		for (int i = 0; i < dimension-1; i++) {
			StringTokenizer tokenizer = new StringTokenizer(in.readLine());
			for (int j = i+1; j < dimension; j++) {
				String weight = tokenizer.nextToken();
				graph.addEdge(new Integer(i), new Long(weight), new Integer(j));
				graph.addEdge(new Integer(j), new Long(weight), new Integer(i));
			}
		}
		return graph;
	}

	private Object read_TSP_EUC_2D(BufferedReader in, int dimension) throws IOException {
		Graph graph = new SimpleGraph();
		List nodes = readNodes(in, 2, dimension);
		for (Iterator it1 = nodes.iterator(); it1.hasNext(); ) {
			List node1 = (List) it1.next();
			for (Iterator it2 = nodes.iterator(); it2.hasNext(); ) {
				List node2 = (List) it2.next();
				if (!node1.equals(node2)) {
					double xd = ((Double) node1.get(1)).doubleValue() - ((Double) node2.get(1)).doubleValue();
					double yd = ((Double) node1.get(2)).doubleValue() - ((Double) node2.get(2)).doubleValue();
					Long weight = new Long(Math.round(Math.sqrt(xd*xd + yd*yd)));
					graph.addEdge(new Long(node1.get(0).toString()), weight, new Long(node2.get(0).toString()));
				}
			}
		}
		return graph;
	}

	private Object read_TSP_CEIL_2D(BufferedReader in, int dimension) throws IOException {
		Graph graph = new SimpleGraph();
		List nodes = readNodes(in, 2, dimension);
		for (int i = 0; i < nodes.size(); i++) {
			List node1 = (List) nodes.get(i);
			for (int j = i+1; j < nodes.size(); j++) {
				List node2 = (List) nodes.get(j);;
				double xd = ((Double) node1.get(1)).doubleValue() - ((Double) node2.get(1)).doubleValue();
				double yd = ((Double) node1.get(2)).doubleValue() - ((Double) node2.get(2)).doubleValue();
				double weight = (Math.sqrt(xd*xd + yd*yd));
				long rounded = (long) weight;
				if (rounded < weight) {
					rounded++;
				}
				graph.addEdge(new Long(node1.get(0).toString()), new Long(rounded), new Long(node2.get(0).toString()));
				graph.addEdge(new Long(node2.get(0).toString()), new Long(rounded), new Long(node1.get(0).toString()));
			}
		}
		
		/* Vers?o n?o otimizada
		for (Iterator it1 = nodes.iterator(); it1.hasNext(); ) {
			List node1 = (List) it1.next();
			for (Iterator it2 = nodes.iterator(); it2.hasNext(); ) {
				List node2 = (List) it2.next();
				if (!node1.equals(node2)) {
					double xd = ((Double) node1.get(1)).doubleValue() - ((Double) node2.get(1)).doubleValue();
					double yd = ((Double) node1.get(2)).doubleValue() - ((Double) node2.get(2)).doubleValue();
					double weight = (Math.sqrt(xd*xd + yd*yd));
					long rounded = (long) weight;
					if (rounded < weight) {
						rounded++;
					}
					graph.addEdge(new Long(node1.get(0).toString()), new Long(rounded), new Long(node2.get(0).toString()));
				}
			}
		}
		*/
		return graph;
	}
	
	private List readNodes(BufferedReader in, int spaceDimension, int quantity) throws IOException {
		List nodes = new Vector(quantity);
		for (int i = 0; i < quantity; i++) {
			StringTokenizer tokenizer = new StringTokenizer(in.readLine());
			List nodeValue = new LinkedList();
			nodeValue.add(tokenizer.nextToken());
			for (int j = 0; j < spaceDimension; j++) {
				nodeValue.add(new Double(tokenizer.nextToken()));
			}
			nodes.add(nodeValue);
		}
		return nodes;
	}

}
